/**
 * Copyright 2019 吉鼎科技.

 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cn.easyplatform.support.scripting.cmd;

import cn.easyplatform.cfg.BusinessDateHandler;
import cn.easyplatform.lang.Strings;
import cn.easyplatform.lang.Times;
import cn.easyplatform.support.scripting.AbstractScriptCmd;
import cn.easyplatform.support.scripting.ScriptCmdContext;
import org.apache.commons.lang3.time.DateFormatUtils;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Currency;
import java.util.Date;


/**
 * @author <a href="mailto:davidchen@epclouds.com">littleDog</a> <br/>
 * @since 2.0.0 <br/>
 */
public class DateCmd extends AbstractScriptCmd {

	private static final int C_ONE_SECOND = 1000;

	private static final int C_ONE_MINUTE = 60 * C_ONE_SECOND;

	private static final long C_ONE_HOUR = 60 * C_ONE_MINUTE;

	private static final long C_ONE_DAY = 24 * C_ONE_HOUR;

	DateCmd(ScriptCmdContext scc) {
		super(scc);
	}

	public boolean isBizDate(Date date) {
		return isBizDate(date, null);
	}

	public boolean isBizDate(Date date, String ccy) {
		if (date == null)
			return false;
		if (Strings.isBlank(ccy))
			ccy = Currency.getInstance(
					scc.getCommandContext().getProjectService().getLocale())
					.getCurrencyCode();
		return scc.getCommandContext().getBusinessDateHandler()
				.isBusinessDate(date, ccy);
	}

	public Date getBizDate(int days) {
		return getBizDate(null, null, days);
	}

	public Date getBizDate(Date date, int days) {
		return getBizDate(null, date, days);
	}

	public Date getBizDate(String ccy, int days) {
		return getBizDate(ccy, null, days);
	}

	public Date getBizDate(String ccy, Date date, int days) {
		if (date == null)
			date = new Date();
		if (days == 0)
			return date;
		if (Strings.isBlank(ccy))
			ccy = Currency.getInstance(
					scc.getCommandContext().getProjectService().getLocale())
					.getCurrencyCode();
		Calendar calendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		calendar.setTime(date);
		BusinessDateHandler bizDateHandler = scc.getCommandContext()
				.getBusinessDateHandler();
		int count = days;
		if (days < 0)
			count = -days;
		int index = 0;
		do {
			calendar.add(Calendar.DAY_OF_MONTH, days < 0 ? -1 : 1);
			if (bizDateHandler.isBusinessDate(calendar.getTime(), ccy))
				index++;
		} while (index != count);
		return calendar.getTime();
	}

	public Date getNextBizDate() {
		return getBizDate(null, null, 1);
	}

	public Date getNextBizDate(Date date) {
		return getBizDate(null, date, 1);
	}

	public Date getNextBizDate(String ccy, Date date) {
		return getBizDate(ccy, date, 1);
	}

	public int getBizDays(Date from, Date to) {
		if (from == null || to == null)
			return 0;
		from = Times.toDay(from);
		to = Times.toDay(to);
		if (from == to)
			return 0;
		if (from.after(to))
			return -1;
		Calendar firstCalendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		firstCalendar.setTime(from);
		int days = 0;
		do {
			firstCalendar.add(Calendar.DATE, 1);
			from = firstCalendar.getTime();
			if (isBizDate(from))
				days++;
		} while (!from.equals(to));
		return days;
	}

	public String format(Date date) {
		return format(date, null);
	}

	public String format(Date date, String pattern) {
		if (date == null)
			return null;
		if (!Strings.isBlank(pattern))
			return DateFormatUtils.format(date, pattern);
		else
			return DateFormat.getDateInstance(DateFormat.MEDIUM,
					scc.getCommandContext().getProjectService().getLocale())
					.format(date);
	}

	public Date toDate(String str) {
		return toDate(str, null);
	}

	public Date toDate(String str, String pattern) {
		if (Strings.isBlank(str))
			return null;
		if (Strings.isBlank(pattern))
			pattern = "yyyyMMdd";
		SimpleDateFormat format = new SimpleDateFormat(pattern);
		try {
			return format.parse(str);
		} catch (ParseException e) {
			return null;
		}
	}

	public Date toDay() {
		return Times.toDay();
	}

	public Date toDay(Date date) {
		return Times.toDay(date);
	}

	public int getHours(Date firstDate, Date secondDate) {
		Calendar firstCalendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		firstCalendar.setTime(firstDate);
		Calendar secondCalendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		secondCalendar.setTime(secondDate);
		if (firstCalendar.after(secondCalendar)) {
			Calendar calendar = firstCalendar;
			firstCalendar = secondCalendar;
			secondCalendar = calendar;
		}
		long calendarNum1 = firstCalendar.getTimeInMillis();
		long calendarNum2 = secondCalendar.getTimeInMillis();
		return Math.abs((int) ((calendarNum1 - calendarNum2) / C_ONE_HOUR));
	}

	public int getDays(Date firstDate, Date secondDate) {
		Calendar firstCalendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		firstCalendar.setTime(firstDate);
		Calendar secondCalendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		secondCalendar.setTime(secondDate);
		if (firstCalendar.after(secondCalendar)) {
			Calendar calendar = firstCalendar;
			firstCalendar = secondCalendar;
			secondCalendar = calendar;
		}
		long calendarNum1 = firstCalendar.getTimeInMillis();
		long calendarNum2 = secondCalendar.getTimeInMillis();
		return Math.abs((int) ((calendarNum1 - calendarNum2) / C_ONE_DAY));
	}

	public int getMonths(Date firstDate, Date secondDate) {
		Calendar firstCalendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		firstCalendar.setTime(firstDate);
		Calendar secondCalendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		secondCalendar.setTime(secondDate);
		if (firstCalendar.after(secondCalendar)) {
			Calendar calendar = firstCalendar;
			firstCalendar = secondCalendar;
			secondCalendar = calendar;
		}
		int flag = 0;
		if (secondCalendar.get(Calendar.DAY_OF_MONTH) < firstCalendar
				.get(Calendar.DAY_OF_MONTH))
			flag = 1;
		if (secondCalendar.get(Calendar.YEAR) > firstCalendar
				.get(Calendar.YEAR))
			return ((secondCalendar.get(Calendar.YEAR) - firstCalendar
					.get(Calendar.YEAR))
					* 12
					+ secondCalendar.get(Calendar.MONTH) - flag)
					- firstCalendar.get(Calendar.MONTH);
		else
			return secondCalendar.get(Calendar.MONTH)
					- firstCalendar.get(Calendar.MONTH) - flag;
	}

	public Date addDays(int day) {
		return addDays(null, day);
	}

	public Date addDays(Date date, int day) {
		if (date == null)
			date = new Date();
		Calendar calendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		calendar.setTime(date);
		calendar.add(Calendar.DATE, day);
		return calendar.getTime();
	}

	public Date addMonths(int months) {
		return addMonths(null, months);
	}

	public Date addMonths(Date date, int months) {
		if (date == null)
			date = new Date();
		Calendar calendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		calendar.setTime(date);
		calendar.add(Calendar.MONTH, months);
		return calendar.getTime();
	}

	public Date addYears(int years) {
		return addYears(null, years);
	}

	public Date addYears(Date date, int years) {
		if (date == null)
			date = new Date();
		Calendar calendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		calendar.setTime(date);
		calendar.add(Calendar.YEAR, years);
		return calendar.getTime();
	}

	public int getMaxDayInMonth(int year, int month) {
		Calendar calendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		if (year == 0 || month == 0) {
			Date date = new Date();
			Calendar c = Calendar.getInstance(scc.getCommandContext()
					.getProjectService().getLocale());
			c.setTime(date);
			if (year == 0)
				year = c.get(Calendar.YEAR);
			if (month == 0)
				month = c.get(Calendar.MONTH - 1);
		}
		calendar.set(Calendar.YEAR, year);
		calendar.set(Calendar.MONTH, month - 1);
		return calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
	}

	public boolean isSameMonth(Date firstDate, Date secondDate) {
		if (firstDate == null || secondDate == null)
			throw new RuntimeException(
					"isSameMonth(firstDate,secondDate),firstDate or secondDate must be not null.");
		Calendar firstCalendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		firstCalendar.setTime(firstDate);
		Calendar secondCalendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		secondCalendar.setTime(secondDate);
		return firstCalendar.get(Calendar.MONTH) == secondCalendar
				.get(Calendar.MONTH);
	}

	public int getRemainingDays(Date date) {
		if (date == null)
			date = new Date();
		Calendar calendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		calendar.setTime(date);
		int maxDate = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
		return maxDate = maxDate - calendar.get(Calendar.DATE);
	}

	public int getYear() {
		return getYear(null);
	}

	public int getYear(Date date) {
		if (date == null)
			date = new Date();
		Calendar calendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		calendar.setTime(date);
		return calendar.get(Calendar.YEAR);
	}

	public int getMonth() {
		return getMonth(null);
	}

	public int getMonth(Date date) {
		if (date == null)
			date = new Date();
		Calendar calendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		calendar.setTime(date);
		return calendar.get(Calendar.MONTH) + 1;
	}

	public int getDay() {
		return getDay(null);
	}

	public int getDay(Date date) {
		if (date == null)
			date = new Date();
		Calendar calendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		calendar.setTime(date);
		return calendar.get(Calendar.DATE);
	}

	public int getDayOfYear() {
		return getDayOfYear(null);
	}

	public int getDayOfYear(Date date) {
		if (date == null)
			date = new Date();
		Calendar calendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		calendar.setTime(date);
		return calendar.get(Calendar.DAY_OF_YEAR);
	}

	public int getDayOfWeek() {
		return getDayOfWeek(null);
	}

	public int getDayOfWeek(Date date) {
		if (date == null)
			date = new Date();
		Calendar calendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		calendar.setTime(date);
		return calendar.get(Calendar.DAY_OF_WEEK);
	}

	public int getWeekOfYear() {
		return getWeekOfYear(null);
	}

	public int getWeekOfYear(Date date) {
		if (date == null)
			date = new Date();
		Calendar calendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		calendar.setTime(date);
		return calendar.get(Calendar.WEEK_OF_YEAR);
	}

	public int getWeekOfMonth() {
		return getWeekOfMonth(null);
	}

	public int getWeekOfMonth(Date date) {
		if (date == null)
			date = new Date();
		Calendar calendar = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		calendar.setTime(date);
		return calendar.get(Calendar.WEEK_OF_MONTH);
	}

	public int getMaxDayInMonth() {
		return getMaxDayInMonth(new Date());
	}

	public int getMaxDayInMonth(Date date) {
		Calendar c = Calendar.getInstance(scc.getCommandContext()
				.getProjectService().getLocale());
		c.setTime(date);
		return c.getActualMaximum(Calendar.DAY_OF_MONTH);
	}

	public Date now(){
		return new Date();
	}
}
